/*

/// disco.js v0.2.1a ///

package of all disco client library scripts
based on the following files in this order:

* disco.core.js
* disco.model.js
* disco.model.helper.js
* disco.web.js
* disco.ontology.js

Note that you have to also include jQuery, Knockout JS and DataJS to get this work!
(Tested with jQuery v1.8.2 and 2.0.1, Knockout v2.2.0 and 2.3.0 and DataJS v1.1.1beta2 and 1.1.1, also included in this folder)

Created with TypeScript (see http://typescriptlang.org/ and *.ts files)

(c) by AG Meinungsfindungstool of the Pirate Party Germany

*/

var disco;
(function (disco) {
    (function (core) {
        var DataManager = (function () {
            function DataManager() {
                this.data = null;
            }
            DataManager.prototype.get = function () {
                return this.data;
            };

            DataManager.prototype.set = function (value) {
                return this.data = value;
            };
            return DataManager;
        })();
        core.DataManager = DataManager;
    })(disco.core || (disco.core = {}));
    var core = disco.core;
})(disco || (disco = {}));

var disco;
(function (disco) {
    var Namespace = (function () {
        function Namespace() {
        }
        Namespace.remove = function (object) {
            return object.substr("Disco.Ontology.".length);
        };
        return Namespace;
    })();
    disco.Namespace = Namespace;

    var Collections = (function () {
        function Collections() {
        }
        Collections.contains = function (collection, predicate) {
            var result = jQuery.grep(collection, predicate);
            return result.length > 0;
        };

        Collections.where = function (collection, predicate) {
            var result = jQuery.grep(collection, predicate);
            return result;
        };

        Collections.single = function (collection, predicate) {
            var result = Collections.nullableSingle(collection, predicate);
            if (result)
                return result;
else
                throw new Error('The given predicate did not match one single item!');
        };

        Collections.nullableSingle = function (collection, predicate) {
            var result = jQuery.grep(collection, predicate);
            if (result.length == 1)
                return result[0];
else if (result.length == 0)
                return null;
else
                throw new Error('The given predicate matched more than one item!');
        };

        Collections.select = function (collection, selector) {
            var result = [];

            for (var item in collection) {
                result.push(selector(collection[item]));
            }

            return result;
        };

        Collections.implode = function (collection, delimiter) {
            var ret = '';

            collection.forEach(function (elem) {
                if (ret != '')
                    ret += delimiter;
                ret += elem;
            });

            return ret;
        };
        return Collections;
    })();
    disco.Collections = Collections;

    var Callbacks = (function () {
        function Callbacks() {
        }
        Callbacks.batch = function (batch, onFinish) {
            if (typeof onFinish === "undefined") { onFinish = null; }
            var _batch = function (i) {
                if (batch.length > i)
                    batch[i](_batch.bind(null, i + 1));
else if (onFinish)
                    onFinish();
            };
            _batch(0);
        };

        Callbacks.allAtOnce = function (callbacks, onFinish) {
            if (typeof onFinish === "undefined") { onFinish = null; }
            var i = callbacks.length;

            if (i <= 0) {
                if (onFinish)
                    onFinish();
            }

            var finishCallback = function () {
                if (--i <= 0 && onFinish)
                    onFinish();
            };

            callbacks.forEach(function (cb) {
                var ready = false;
                cb(function () {
                    if (!ready) {
                        ready = true;
                        finishCallback();
                    }
                });
            });
        };
        return Callbacks;
    })();
    disco.Callbacks = Callbacks;

    var Type = (function () {
        function Type() {
        }
        Type.get = function (variable) {
            return Object.prototype.toString.call(variable);
        };
        return Type;
    })();
    disco.Type = Type;

    var Filters = (function () {
        function Filters() {
        }
        Filters.valueToOdata = function (value) {
            if (typeof value == 'number')
                return (value).toString();
            if (typeof value == 'string')
                return "'" + value + "'";
else
                throw new Error();
        };
        return Filters;
    })();
    disco.Filters = Filters;
})(disco || (disco = {}));
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var disco;
(function (disco) {
    (function (model) {
        (function (helper) {
            var DataContextManager = (function () {
                function DataContextManager(dataContext) {
                    this.dataContext = dataContext;
                }
                DataContextManager.prototype.getDataContext = function () {
                    return this.dataContext;
                };

                DataContextManager.prototype.setDataContext = function (context) {
                    this.dataContext = context;
                };

                DataContextManager.prototype.getMetadata = function () {
                    return this.getDataContext().metadata;
                };

                DataContextManager.prototype.generateEntityModel = function () {
                    this.generateEntityTypes();
                    this.generateEntitySets();
                };

                DataContextManager.prototype.generateEntitySets = function () {
                    var _this = this;
                    var context = this.getDataContext();
                    var metaEntitySets = this.getEntitySetsMetadata();

                    metaEntitySets.forEach(function (metaEntitySet) {
                        var setTypeName = disco.Namespace.remove(metaEntitySet.entityType);

                        var set = new model.EntitySet();
                        set.name = metaEntitySet.name;
                        set.type = _this.getDataContext().entityType(setTypeName);
                        set.type.entitySet = set;

                        _this.getDataContext().entitySet(set.type.name, set);
                    });
                };

                DataContextManager.prototype.generateEntityTypes = function () {
                    this.generateBasicEntityTypes();
                    this.applyBaseTypes();
                    this.addNavigationToEntityProperties();
                };

                DataContextManager.prototype.generateBasicEntityTypes = function () {
                    var _this = this;
                    var associations = this.getAssociationsMetadata();
                    var typeMgr = new model.EntityTypeManager();

                    this.getEntityTypesMetadata().forEach(function (metaEntityType) {
                        typeMgr.setEntityType();
                        typeMgr.generateSimpleProperties(metaEntityType, associations);

                        var type = typeMgr.getEntityType();
                        _this.getDataContext().entityType(type.name, type);
                    });
                };

                DataContextManager.prototype.applyBaseTypes = function () {
                    var _this = this;
                    var typeMgr = new model.EntityTypeManager();

                    Object.keys(this.getDataContext().entityTypes).forEach(function (key) {
                        typeMgr.setEntityType(_this.getDataContext().entityTypes[key]);
                        typeMgr.applyBaseType(_this.getDataContext());
                    });
                };

                DataContextManager.prototype.addNavigationToEntityProperties = function () {
                    var _this = this;
                    var typeMgr = new model.EntityTypeManager();

                    Object.keys(this.getDataContext().entityTypes).forEach(function (key) {
                        typeMgr.setEntityType(_this.getDataContext().entityTypes[key]);
                        typeMgr.addNavigationToEntityProperties(_this.getDataContext());
                    });
                };

                DataContextManager.prototype.getEntityTypesMetadata = function () {
                    return this.getMetadataServices().schema[0].entityType;
                };
                DataContextManager.prototype.getAssociationsMetadata = function () {
                    return this.getMetadataServices().schema[0].association;
                };
                DataContextManager.prototype.getEntitySetsMetadata = function () {
                    var dataServices;
                    var schemaIndex = (this.getMetadataServices().schema.length > 1) ? 1 : 0;
                    return this.getMetadataServices().schema[schemaIndex].entityContainer[0].entitySet;
                };

                DataContextManager.prototype.getMetadataServices = function () {
                    return this.getMetadata().dataServices;
                };
                return DataContextManager;
            })();
            helper.DataContextManager = DataContextManager;
            var EntitySetManager = (function (_super) {
                __extends(EntitySetManager, _super);
                function EntitySetManager() {
                    _super.apply(this, arguments);
                }
                EntitySetManager.prototype.add = function (entity) {
                    if (!disco.Collections.contains(this.get().rows(), function (e) {
                        return e.prop('Id').value == entity.prop('Id').value;
                    }))
                        this.get().rows.push(entity);
                };
                return EntitySetManager;
            })(disco.core.DataManager);
            helper.EntitySetManager = EntitySetManager;
            var EntityManager = (function (_super) {
                __extends(EntityManager, _super);
                function EntityManager(context) {
                    _super.call(this);
                    this.context = context;
                }
                EntityManager.prototype.fromRawEntity = function (rawEntity) {
                    var entity = this.set(new model.Entity());

                    var typeString = disco.Namespace.remove(rawEntity['odata.type']);
                    entity.type = this.context.entityType(typeString);
                    entity.type.properties.forEach(function (property) {
                        var propertyValue = new disco.model.PropertyValue();
                        propertyValue.property = property;
                        if (property.isNavigation) {
                            propertyValue.url = rawEntity[property.name + '@odata.navigationLinkUrl'];
                        } else {
                            propertyValue.value = rawEntity[property.name];
                        }
                        entity.prop(property.name, propertyValue);
                    });

                    return entity;
                };
                return EntityManager;
            })(disco.core.DataManager);
            helper.EntityManager = EntityManager;
        })(model.helper || (model.helper = {}));
        var helper = model.helper;
    })(disco.model || (disco.model = {}));
    var model = disco.model;
})(disco || (disco = {}));
var disco;
(function (disco) {
    (function (web) {
        var Requests = (function () {
            function Requests() {
            }
            Requests.loadOntologyMetadata = function (url, success, error) {
                if (typeof error === "undefined") { error = null; }
                OData.read(url, success, error, OData.metadataHandler);
            };

            Requests.loadOntologyContent = function (url, success, error) {
                if (typeof error === "undefined") { error = null; }
                jQuery.ajax({
                    headers: { Accept: "application/json;odata=fullmetadata" },
                    url: url,
                    success: success,
                    error: function (a, b, err) {
                        if (error)
                            error(err);
                    },
                    cache: false
                });
            };

            Requests.addEntity = function (entitySetUrl, propertyValues, success, error) {
                if (typeof error === "undefined") { error = null; }
                jQuery.ajax({
                    headers: { Accept: "application/json;odata=fullmetadata" },
                    url: entitySetUrl,
                    type: "POST",
                    data: propertyValues,
                    success: success,
                    error: function (a, b, err) {
                        if (error)
                            error(err);
                    },
                    cache: false
                });
            };

            Requests.removeEntity = function (entityUrl, success, error) {
                if (typeof error === "undefined") { error = null; }
                jQuery.ajax({
                    url: entityUrl,
                    type: "DELETE",
                    success: success,
                    error: function (a, b, err) {
                        if (error)
                            error(err);
                    },
                    cache: false
                });
            };

            Requests.addEntityConnection = function (entityUrl, propertyUrl, valueUrl, success, error) {
                if (typeof error === "undefined") { error = null; }
                jQuery.ajax({
                    url: entityUrl + '/$links/' + propertyUrl,
                    type: "POST",
                    data: JSON.stringify({ url: valueUrl }),
                    processData: false,
                    contentType: 'application/json; charset=UTF-8',
                    success: success,
                    error: function (x, y, e) {
                        if (error)
                            error(e);
                    }
                });
            };

            Requests.removeEntityConnection = function (entityUrl, propertyUrlWithKey, success, error) {
                if (typeof error === "undefined") { error = null; }
                jQuery.ajax({
                    url: entityUrl + '/$links/' + propertyUrlWithKey,
                    type: "DELETE",
                    success: success,
                    error: function (x, y, e) {
                        if (error)
                            error(e);
                    }
                });
            };
            return Requests;
        })();
        web.Requests = Requests;
    })(disco.web || (disco.web = {}));
    var web = disco.web;
})(disco || (disco = {}));
var disco;
(function (disco) {
    var Ontology = (function () {
        function Ontology(serviceUri) {
            this.serviceUri = serviceUri;
        }
        Ontology.prototype.isInitialized = function () {
            return this.dataContext != null;
        };

        Ontology.prototype.init = function (success, error) {
            var _this = this;
            disco.web.Requests.loadOntologyMetadata(this.serviceUri + "$metadata", function (metadata) {
                var context = new disco.model.DataContext();
                var contextMgr = new disco.model.helper.DataContextManager(context);

                context.serviceUri = _this.serviceUri;
                context.metadata = metadata;
                contextMgr.generateEntityModel();

                _this.dataContext = context;
                success(context);
            }, error);
        };

        Ontology.prototype.getEntityById = function (type, id, success, error) {
            var set = this.dataContext.entitySet(type.name);

            var existing = disco.Collections.nullableSingle(set.rows(), function (e) {
                return e.prop('Id').value == id;
            });
            if (existing != null)
                success(existing);
else
                this.getEntityByPath(set.name + '(' + id + ')', success, error);
        };

        Ontology.prototype.getEntityByPath = function (path, success, error) {
            var _this = this;
            disco.web.Requests.loadOntologyContent(this.serviceUri + path, function (data) {
                success(_this.registerFromRawEntity(data));
            }, error);
        };

        Ontology.prototype.getEntitiesByPath = function (path, success, error) {
            this.getEntitiesByUrl(this.serviceUri + path, success, error);
        };

        Ontology.prototype.getEntitiesByUrl = function (url, success, error) {
            if (typeof error === "undefined") { error = null; }
            var _this = this;
            disco.web.Requests.loadOntologyContent(url, function (data) {
                var rawEntities = data.value || [data];
                var entities = [];

                rawEntities.forEach(function (rawEntity) {
                    entities.push(_this.registerFromRawEntity(rawEntity));
                });

                success(entities);
            }, error);
        };

        Ontology.prototype.registerFromRawEntity = function (rawEntity) {
            var entityMgr = new disco.model.helper.EntityManager(this.dataContext);
            var entity = entityMgr.fromRawEntity(rawEntity);

            var en = disco.Collections.nullableSingle(entity.type.entitySet.rows(), function (e) {
                return e.prop('Id').value == entity.prop('Id').value;
            });
            if (en)
                return en;

            var setMgr = new disco.model.helper.EntitySetManager();
            setMgr.set(this.dataContext.entitySet(entity.type.name));

            setMgr.add(entity);
            return entity;
        };

        Ontology.prototype.getEntitiesByTemplate = function (template, success, error, url) {
            if (typeof error === "undefined") { error = null; }
            if (typeof url === "undefined") { url = null; }
            var subEntities = [];
            var mode = (url == null) ? 'entitySet' : 'url';

            var constraints = {};
            var templates = {};
            var computedUrl;

            Object.keys(template.properties || {}).forEach(function (key) {
                var val = template.properties[key];

                if ((typeof val) == 'object' && disco.Type.get(val) != '[object Array]')
                    templates[key] = val;
else
                    constraints[key] = val;
            });

            if (mode == 'entitySet') {
                var filterPath = '?$filter=true';
                Object.keys(constraints).forEach(function (key) {
                    var val = constraints[key];
                    var str = val;
                    if (disco.Type.get(val) != '[object Array]')
                        val = [val];
                    str = '(' + disco.Collections.implode(val.map(function (v) {
                        return key + ' eq ' + disco.Filters.valueToOdata(v);
                    }), ' or ') + ')';
                    filterPath += ' and ' + str;
                });
                computedUrl = this.serviceUri + (template.type).entitySet.name + filterPath;
            } else if (mode == 'url') {
                computedUrl = url;
            }

            var self = this;

            this.getEntitiesByUrl(computedUrl, function (entities) {
                var entityCallbacks = entities.map(function (entity) {
                    return (function (onEntityReady) {
                        var subEntity = new disco.model.SubEntity(entity);
                        var arr = Object.keys(constraints);
                        for (var i = 0; i < arr.length; ++i) {
                            var key = arr[i];
                            var val = constraints[key];
                            if (entity.prop(key).value != val) {
                                onEntityReady();
                                return;
                            }
                        }
                        subEntities.push(subEntity);
                        var propertyCallbacks = Object.keys(templates).map(function (key) {
                            return (function (onPropertyReady) {
                                var property = entity.prop(key);
                                var template = templates[key];
                                if (!property.property.isNavigation || property.value) {
                                    onPropertyReady();
                                    return;
                                }
                                self.getEntitiesByTemplate(template, function (_subEntities, _entities) {
                                    var pv = new disco.model.PropertyValue(property);
                                    if (property.property.isMany) {
                                        pv.value = _subEntities;
                                    } else {
                                        if (_entities.length > 0)
                                            property.value = _entities[0];
                                        if (_subEntities.length > 0)
                                            pv.value = _subEntities[0];
else
                                            pv.value = null;
                                    }
                                    subEntity.prop(key, pv);
                                    onPropertyReady();
                                }, null, property.url);
                            });
                        });
                        disco.Callbacks.allAtOnce(propertyCallbacks, function () {
                            onEntityReady();
                        });
                    });
                });
                disco.Callbacks.allAtOnce(entityCallbacks, function () {
                    success(subEntities, entities);
                });
            }, function (error) {
                return error;
            });
        };

        Ontology.prototype.addEntity = function (entityTemplate, success, error) {
            var _this = this;
            disco.web.Requests.addEntity(this.dataContext.serviceUri + entityTemplate.type.entitySet.name, entityTemplate.properties || {}, function (rawEntity) {
                success(_this.registerFromRawEntity(rawEntity));
            }, function (err) {
                error(err);
            });
        };

        Ontology.prototype.removeEntity = function (entityType, entityId, success, error) {
            this.removeEntityByPath(entityType.entitySet.name + '(' + entityId + ')', success, error);
        };

        Ontology.prototype.removeEntityByPath = function (path, success, error) {
            this.removeEntityByUrl(this.dataContext.serviceUri + path, success, error);
        };

        Ontology.prototype.removeEntityByUrl = function (url, success, error) {
            disco.web.Requests.removeEntity(url, function (d) {
                success();
            }, function (e) {
                error(e);
            });
        };

        Ontology.prototype.addConnection = function (entityType, entityId, property, valueId, success, error) {
            if (!property.isNavigation)
                throw new Error('disco.Ontology.addConnection: operation is only valid for navigation properties.');
            disco.web.Requests.addEntityConnection(this.dataContext.serviceUri + entityType.entitySet.name + '(' + entityId + ')', property.name, this.dataContext.serviceUri + property.type.entitySet.name + '(' + valueId + ')', success, error);
        };

        Ontology.prototype.removeConnection = function (entityType, entityId, property, valueId, success, error) {
            if (!property.isNavigation)
                throw new Error('disco.Ontology.addConnection: operation is only valid for navigation properties.');
            disco.web.Requests.removeEntityConnection(this.dataContext.serviceUri + entityType.entitySet.name + '(' + entityId + ')', property.name + '(' + valueId + ')', success, error);
        };

        Ontology.prototype.clearCache = function () {
            var _this = this;
            Object.keys(this.dataContext.entitySets).forEach(function (key) {
                var set = _this.dataContext.entitySets[key];
                set.rows([]);
            });
        };
        return Ontology;
    })();
    disco.Ontology = Ontology;
})(disco || (disco = {}));
var disco;
(function (disco) {
    (function (model) {
        var Metadata = (function () {
            function Metadata() {
            }
            return Metadata;
        })();
        model.Metadata = Metadata;

        var DataContext = (function () {
            function DataContext() {
                this.entityTypes = {};
                this.entitySets = {};
            }
            DataContext.prototype.entityType = function (name, value) {
                if (typeof value === "undefined") { value = undefined; }
                if (value)
                    return this.entityTypes[name] = value;
else
                    return this.entityTypes[name];
            };

            DataContext.prototype.entitySet = function (typeName, value) {
                if (typeof value === "undefined") { value = undefined; }
                if (value)
                    return this.entitySets[typeName] = value;
else
                    return this.entitySets[typeName];
            };
            return DataContext;
        })();
        model.DataContext = DataContext;

        var EntityType = (function () {
            function EntityType() {
                this.entitySet = null;
                this.properties = [];
                this.navigationProperties = [];
                this.baseType = null;
                this.metadata = null;
                this.useToStringForStringify = true;
            }
            EntityType.prototype.toString = function () {
                return '<' + this.name + '>';
            };
            return EntityType;
        })();
        model.EntityType = EntityType;

        var EntityProperty = (function () {
            function EntityProperty() {
                this.isInherited = false;
                this.isKey = false;
                this.isNullable = true;
                this.isMany = false;
                this.type = null;
                this.isNavigation = false;
                this.useToStringForStringify = true;
            }
            EntityProperty.prototype.toString = function () {
                return '<' + this.name + ': ' + this.typeString + '>';
            };
            return EntityProperty;
        })();
        model.EntityProperty = EntityProperty;

        var NavigationProperty = (function () {
            function NavigationProperty() {
                this.inherited = false;
            }
            return NavigationProperty;
        })();
        model.NavigationProperty = NavigationProperty;

        var AssociationEnd = (function () {
            function AssociationEnd() {
            }
            return AssociationEnd;
        })();
        model.AssociationEnd = AssociationEnd;

        var EntityCollection = (function () {
            function EntityCollection() {
                this.rows = ko.observableArray([]);
            }
            return EntityCollection;
        })();
        model.EntityCollection = EntityCollection;

        var EntitySet = (function (_super) {
            __extends(EntitySet, _super);
            function EntitySet() {
                _super.apply(this, arguments);
            }
            return EntitySet;
        })(EntityCollection);
        model.EntitySet = EntitySet;

        var Entity = (function () {
            function Entity() {
                this.properties = {};
                this.isEntity = true;
            }
            Entity.prototype.prop = function (name, value) {
                if (typeof value === "undefined") { value = undefined; }
                if (value)
                    return this.properties[name] = value;
else
                    return this.properties[name];
            };
            return Entity;
        })();
        model.Entity = Entity;

        var PropertyValue = (function () {
            function PropertyValue(prop) {
                if (prop) {
                    this.property = prop.property;
                    this.url = prop.url;
                    if (prop.property.isMany)
                        this.value = [];
                }
            }
            PropertyValue.prototype.prop = function (name) {
                return this.value.prop(name);
            };
            return PropertyValue;
        })();
        model.PropertyValue = PropertyValue;

        var OntologyModel = (function () {
            function OntologyModel() {
                this.dataContext = ko.observable();
            }
            OntologyModel.prototype.getDataContext = function () {
                return this.dataContext;
            };

            OntologyModel.prototype.load = function (serviceUri, success) {
                if (typeof success === "undefined") { success = function (o) {
                }; }
                var _this = this;
                OData.read(serviceUri + "$metadata", function (data, response) {
                    var context = new DataContext();
                    context.serviceUri = serviceUri;
                    context.metadata = data;

                    var contextMgr = new model.helper.DataContextManager(context);
                    contextMgr.generateEntityModel();

                    _this.dataContext(context);
                    success(_this);
                }, function (error) {
                }, OData.metadataHandler);
            };
            return OntologyModel;
        })();
        model.OntologyModel = OntologyModel;

        var EntityTypeManager = (function () {
            function EntityTypeManager(entityType) {
                if (typeof entityType === "undefined") { entityType = null; }
                this.entityType = entityType;
            }
            EntityTypeManager.prototype.getEntityType = function () {
                return this.entityType;
            };
            EntityTypeManager.prototype.setEntityType = function (value) {
                if (typeof value === "undefined") { value = new EntityType(); }
                return this.entityType = value;
            };

            EntityTypeManager.prototype.each = function (types, callback) {
                var _this = this;
                types.forEach(function (type, index) {
                    _this.setEntityType(type);
                    callback.call(_this, _this, index, types);
                }, this);
            };

            EntityTypeManager.prototype.generateSimpleProperties = function (metaEntityType, associations) {
                var type = this.getEntityType();
                type.metadata = metaEntityType;
                type.name = metaEntityType.name;

                if (type.metadata.property) {
                    var keyPropertyName = type.metadata.key ? type.metadata.key.propertyRef.name : null;

                    var propertyMgr = new EntityPropertyManager();

                    type.metadata.property.forEach(function (metaProperty) {
                        propertyMgr.generateEntityProperty(metaProperty, keyPropertyName);
                        type.properties.push(propertyMgr.getEntityProperty());
                    });
                }
                if (type.metadata.navigationProperty) {
                    var navigationPropertyMgr = new NavigationPropertyManager();

                    type.metadata.navigationProperty.forEach(function (metaProperty) {
                        navigationPropertyMgr.generateNavigationProperty(metaProperty, associations);
                        type.navigationProperties.push(navigationPropertyMgr.getNavigationProperty());
                    });
                }
            };
            EntityTypeManager.prototype.applyBaseType = function (context) {
                var type = this.getEntityType();
                if (type && type.metadata.baseType && !type.baseType) {
                    var baseTypeName = disco.Namespace.remove(type.metadata.baseType);
                    type.baseType = context.entityType(baseTypeName);

                    new EntityTypeManager(type.baseType).applyBaseType(context);

                    var entityPropertyMgr = new EntityPropertyManager();
                    var inheritedEntityProperties = entityPropertyMgr.copyProperties(type.baseType.properties);
                    inheritedEntityProperties.forEach(function (property) {
                        property.isInherited = true;
                    });
                    type.properties = inheritedEntityProperties.concat(type.properties);

                    var navigationPropertyMgr = new NavigationPropertyManager();
                    var inheritedNavigationProperties = navigationPropertyMgr.copyProperties(type.baseType.navigationProperties);
                    inheritedNavigationProperties.forEach(function (property) {
                        property.inherited = true;
                    });
                    type.navigationProperties = inheritedNavigationProperties.concat(type.navigationProperties);
                }
            };
            EntityTypeManager.prototype.addNavigationToEntityProperties = function (context) {
                var entityPropertyMgr = new EntityPropertyManager();
                var type = this.getEntityType();

                var convertedProperties = type.navigationProperties.map(function (navigationProperty) {
                    return entityPropertyMgr.fromNavigationProperty(navigationProperty, context);
                });
                type.properties = type.properties.concat(convertedProperties);
            };
            return EntityTypeManager;
        })();
        model.EntityTypeManager = EntityTypeManager;

        var EntityPropertyManager = (function () {
            function EntityPropertyManager(entityProperty) {
                if (typeof entityProperty === "undefined") { entityProperty = null; }
                this.entityProperty = entityProperty;
            }
            EntityPropertyManager.prototype.getEntityProperty = function () {
                return this.entityProperty;
            };

            EntityPropertyManager.prototype.setEntityProperty = function (entityProperty) {
                if (typeof entityProperty === "undefined") { entityProperty = new EntityProperty(); }
                return this.entityProperty = entityProperty;
            };

            EntityPropertyManager.prototype.generateEntityProperty = function (metaProperty, keyPropertyName) {
                var property = this.setEntityProperty();
                property.name = metaProperty.name;
                property.typeString = metaProperty.type;
                property.isNullable = metaProperty.nullable || true;
                property.isKey = metaProperty.name == keyPropertyName;
            };

            EntityPropertyManager.prototype.fromNavigationProperty = function (navigationProperty, context) {
                var entityProperty = this.setEntityProperty();
                var associationEnd = navigationProperty.to;

                entityProperty.isNavigation = true;
                entityProperty.name = associationEnd.role;
                entityProperty.isMany = associationEnd.multiplicity == '*';
                entityProperty.isNullable = associationEnd.multiplicity != '1';
                entityProperty.typeString = disco.Namespace.remove(associationEnd.type);
                entityProperty.type = context.entityType(entityProperty.typeString);

                return entityProperty;
            };

            EntityPropertyManager.prototype.copyProperty = function (property) {
                var p = property;
                var ret = {
                    isInherited: p.isInherited,
                    isKey: p.isKey,
                    isMany: p.isMany,
                    isNavigation: p.isNavigation,
                    isNullable: p.isNullable,
                    name: p.name,
                    type: p.type,
                    typeString: p.typeString,
                    toString: EntityProperty.prototype.toString,
                    useToStringForStringify: p.useToStringForStringify
                };
                return this.setEntityProperty(ret);
            };
            EntityPropertyManager.prototype.copyProperties = function (properties) {
                var self = this;
                return properties.map(function (property) {
                    return self.copyProperty(property);
                });
            };
            return EntityPropertyManager;
        })();
        model.EntityPropertyManager = EntityPropertyManager;

        var NavigationPropertyManager = (function () {
            function NavigationPropertyManager(navigationProperty) {
                if (typeof navigationProperty === "undefined") { navigationProperty = null; }
                this.navigationProperty = navigationProperty;
            }
            NavigationPropertyManager.prototype.getNavigationProperty = function () {
                return this.navigationProperty;
            };

            NavigationPropertyManager.prototype.setNavigationProperty = function (property) {
                if (typeof property === "undefined") { property = new NavigationProperty(); }
                return this.navigationProperty = property;
            };

            NavigationPropertyManager.prototype.generateNavigationProperty = function (metaProperty, metaAssociations) {
                var property = this.setNavigationProperty();
                property.name = metaProperty.name;

                var metaAssociationName = disco.Namespace.remove(metaProperty.relationship);
                var metaAssociation = disco.Collections.single(metaAssociations, function (metaAssociation) {
                    return metaAssociationName == metaAssociation.name;
                });
                property.from = disco.Collections.single(metaAssociation.end, function (associationEnd) {
                    return metaProperty.fromRole == associationEnd.role;
                });
                property.to = disco.Collections.single(metaAssociation.end, function (associationEnd) {
                    return metaProperty.toRole == associationEnd.role;
                });
            };

            NavigationPropertyManager.prototype.copyProperty = function (property) {
                var p = property;
                var ret = {
                    from: p.from,
                    inherited: p.inherited,
                    name: p.name,
                    to: p.to
                };
                return this.setNavigationProperty(ret);
            };

            NavigationPropertyManager.prototype.copyProperties = function (properties) {
                var self = this;
                return properties.map(function (property) {
                    return self.copyProperty(property);
                });
            };
            return NavigationPropertyManager;
        })();
        model.NavigationPropertyManager = NavigationPropertyManager;

        model.stringifyReplacer = function (key, value) {
            var replacePropertyValue = function (val) {
                if (typeof val == 'object') {
                    if (val.isEntity) {
                        return '<' + val.type.name + ' #' + val.prop('Id').value + '>';
                    } else if (disco.Type.get(val) == '[object Array]') {
                        return (val).map(replacePropertyValue);
                    } else
                        return val;
                } else
                    return val;
            };

            if (value && value.useToStringForStringify) {
                return value.toString();
            }
            if (key == 'value')
                return replacePropertyValue(value);
            return value;
        };

        var SubEntity = (function () {
            function SubEntity(entity) {
                var _this = this;
                this.entity = function (val) {
                    if (val) {
                        _this._entity = val;
                        _this.type = _this._entity.type;
                    }
                    return _this._entity;
                };
                this.props = {};
                this.isEntity = true;
                this.entity(entity);
            }
            SubEntity.prototype.prop = function (name, val) {
                if (typeof val === "undefined") { val = undefined; }
                if (!val) {
                    if (Object.keys(this.props).indexOf(name) != -1)
                        return this.props[name];
else
                        return this.entity().prop(name);
                } else
                    this.props[name] = val;
            };
            return SubEntity;
        })();
        model.SubEntity = SubEntity;
    })(disco.model || (disco.model = {}));
    var model = disco.model;
})(disco || (disco = {}));
//# sourceMappingURL=disco-0.3.5077.29162.js.map
